require 'curb'

require 'json'
require 'time'
require 'yaml'  # for debug

#class String
#    def to_json(*)
#        # Bugos a szerver oldal, nem fogadja az escape -elt cuccot,
#        # sima UTF-8 stringet kap.
#        #puts "to_json.............. #{self.inspect}"
#        '"' << self << '"'
#    end
#end
#j "árvíztűrő tükörfúrógép"

module App
    class Toggl
        # https://www.toggl.com/en_US/public/api
        #
        # 2009-06-02 0.16.12 -> 1.13.2
        # 2010-07-06 -> v2
        #
        Url = "https://www.toggl.com"

        attr_accessor :email, :password
        attr_accessor :hide_older_than


        def login
            #tzoffset = Time.zone_offset(Time.now.zone)/60/60
            req = JSON.generate(
                "email" => (@email or ""),
                "password" => (@password or "")
            )

            puts "Curl version #{Curl::VERSION}, Curb version #{Curl::CURB_VERSION}"

            @c = Curl::Easy.new
            #@c.verbose = true
            #@c.on_debug { |type, data| puts type.to_s + " :: " + data.to_s }
            @c.enable_cookies = true

            sendreq "/api/v2/sessions.json", :post, req
        end


        def clientlist()
            rep = sendreq "/api/v2/clients.json"
            return rep["data"]
        end

        def projlist()
            rep = sendreq "/api/v2/projects.json"
            return rep["data"]
        end

        def tasklist()
            rep = sendreq "/api/v2/tasks.json?start_date="+(Time.now - hide_older_than*60*60*24).iso8601
            return rep["data"]
        end


        def startTask(task)
            req = JSON.generate(
                "task" => {
                    "duration" => -Time.now.to_i + task.duration,
                    "start" => task.start_time.iso8601,
                    "stop" => nil,
                    "description" => task.description,
                    "project" => {
                        "id" => task.project_id
                    }
                }
            )
            rep = sendreq "/api/v2/tasks/#{task.tid}.json", :put, req
            return rep["data"]
        end

        def stopTask(task)
            req = JSON.generate(
                "task" => {
                    "duration" => task.get_duration,
                    "start" => task.start_time.iso8601,
                    "stop" => Time.now.iso8601,
                    "description" => task.description,
                    "project" => {
                        "id" => task.project_id
                    }
                }
            )
            rep = sendreq "/api/v2/tasks/#{task.tid}.json", :put, req
            return rep["data"]
        end

        def setTask(task, project)
            req = JSON.generate(
                "task" => {
                    "start" => task.start_time.iso8601,
                    "stop" => task.stop_time.iso8601,
                    "description" => task.description,
                    "duration" => task.duration,
                    "project" => {
                        "id" => project.tid
                    }
                }
            )
            rep = sendreq "/api/v2/tasks/#{task.tid}.json", :put, req
        end

        def newTask(descr, project)
            req = JSON.generate(
                "task" => {
                    # Az eddig belefeccolt ido: uj feladatnal mindig nulla
                    "start" => Time.now.iso8601,
                    "description" => descr,
                    "duration" => 0,
                    "billable" => project.billable,
                    "id" => -1,
                    "tag_names" => [],
                    "created_with" => "ktoggl",
                    "project" => {
                        "id" => project.tid
                }
                }
            )
            #p req
            rep = sendreq "/api/v2/tasks.json", :post, req
            return rep["data"]
        end

        def deleteTask(task)
            rep = sendreq "/api/v2/tasks/#{task.tid}.json", :delete
        end


        def newProject(name, billable, client)
            # Old Toggl API bug: the 'billable' attribute were ignored.
            req = JSON.generate(
                "project" => {
                    "billable" => billable,
                    "client" => {
                        "id" => client.tid
                    },
                    "name" => name
                }
            )
            #p req
            rep = sendreq "/api/v2/projects.json", :post, req
            #p rep
            return rep
        end

        def setProject(project, client)
            req = JSON.generate(
                "billable" => project.billable,
                "client" => {
                    "id" => client.tid
                },
                "name" => project.name,
                "id" => project.tid
            )
            rep = sendreq "/api/v2/projects.json", :put, req
        end

        def deleteProject(project)
            raise "No API support!"
        end


        def newClient(name)
            raise "No API support!"
        end

        def deleteClient(client)
            raise "No API support!"
        end

        def setClient(client)
            raise "No API support!"
        end


        private
        def sendreq(path, method = nil, req = nil)
            login unless @c
            
            @c.url = Url + path
            @c.headers["Accept"] = "application/json"
            @c.headers["Content-type"] = "application/json"
            begin
                if req
                    if method == :put
                        @c.http_put req
                    else
                        @c.http_post req
                    end
                else
                    if method == :delete
                        @c.http_delete
                    else
                        @c.http_get
                    end
                end
            rescue Curl::Err
                p "sendreq " << $!
                raise CommunicationError.new($!)
            end

            reponse_code_string = @c.header_str.scan(/.*$/).first.chomp
            case @c.response_code
            when 200 then true
            when 400
                raise CommunicationError.new("reply: #{reponse_code_string}  (#{@c.body_str})")
            when 403
                @c = false
                raise LoginError.new(reponse_code_string)
            else
                p "--- REQ ---", @c.url, req
                p "-- HEADER --", @c.header_str
                p "-- BODY --", @c.body_str
                raise CommunicationError.new("unknown status code")
            end

            reply = @c.body_str
            begin
                ret = JSON.parse reply
            rescue JSON::ParserError
                p "--- REQ ---", @c.url, req
                p "-- HEADER --", @c.header_str
                p "-- BODY --", reply
                raise
            end

            unless ret.is_a? Hash and ret["data"]
                raise CommunicationError.new("reply: "+ret.inspect)
            end

            ret
        end
    end
end
